/*
 * @Description: 
 * @Author: Junhuang
 * @Date: 2022-03-25 17:32:21
 * @LastEditTime: 2022-03-26 21:33:33
 * @LastEditors: Junhuang
 * @Reference: 
 */

const Service = require('egg').Service;
var crypto = require('crypto');
var COS = require('cos-nodejs-sdk-v5');
var cf = require('../../config/config.default');
const { join } = require('path');
var xmlreader = require("xmlreader");
const await = require('await-stream-ready/lib/await');
var cos = null;

class CosService extends Service {
  constructor(props) {
    super(props);
    this.creatCOS();
  }
  // 统一构造COS
  creatCOS() {
    if (!cos) {
      let { secret_id, secret_key } = this.app.config.tencentCos;
      cos = new COS({
        SecretId: secret_id,
        SecretKey: secret_key,//私钥
      });
    }
  }
  // 将服务器文件上传到腾讯云cos的方法
  async docos(filename, target) {
    // this.creatCOS();
    let { bucket, region } = this.app.config.tencentCos;
    return new Promise((resolve, reject) => {
      cos.uploadFile({
        Bucket: bucket, /* 填入您自己的存储桶，必须字段 */
        Region: region,  /* 存储桶所在地域，例如ap-beijing，必须字段 */
        Key: filename,  /* 存储在桶里的对象键（例如1.jpg，a/b/test.txt），必须字段 */
        FilePath: target,                /* 必须 */
        SliceSize: 1024 * 1024 * 5,     /* 触发分块上传的阈值，超过5MB使用分块上传，非必须 */
        onTaskReady: function (taskId) {                   /* 非必须 */
          //console.log('taskId', taskId);
        },
        onProgress: function (progressData) {           /* 非必须 */
          //console.log('progressData', JSON.stringify(progressData));
        },
        onFileFinish: function (err, data, options) {
          console.log(options.Key + '上传' + (err ? '失败' : '完成'));
        },
      }, function (err, data) {
        //console.log(err || data);
        if (err) {
          reject(err);
        } else {
          resolve(data);
        }
      });
    })
  }

  // 将服务器文件上传到腾讯云cos的方法
  async deleteObject(filename) {
    this.creatCOS();
    let { bucket, region } = this.app.config.tencentCos;
    return new Promise((resolve, reject) => {
      cos.deleteObject({
        Bucket: bucket, /* 填入您自己的存储桶，必须字段 */
        Region: region,  /* 存储桶所在地域，例如ap-beijing，必须字段 */
        Key: filename,  /* 存储在桶里的对象键（例如1.jpg，a/b/test.txt），必须字段 */
      }, function (err, data) {
        //console.log(err || data);
        if (err) {
          reject(err);
          console.log('删除对象：' + filename + ',失败' + err)
        } else {
          resolve(data);
          //console.log('删除返回：', data);
          if (data.statusCode == 200 || data.statusCode == 204) {
            console.log('删除对象：' + filename + ',成功')
          } else {
            console.log('删除异常：', data)
          }
        }
      });
    })
  }

  // 图片同步内容审核-API
  // ,Key为文件路径，比如：/1.jpg 或 /darklight/bg.jpg
  async pic_ContentReview(Key) {
    let { secret_id, secret_key, bucket, region } = this.app.config.tencentCos;
    let cos_url = `https://${bucket}.cos.${region}.myqcloud.com`;
    if (!Key.startsWith('/')) {
      Key = '/' + Key;
    }
    console.log('key=', Key);
    let Authorization = this.getAuth({ SecretId: secret_id, SecretKey: secret_key, Key, Bucket: bucket, Region: region });
    console.log('Authorization', Authorization);
    return new Promise((resolve, reject) => {
      let url = cos_url + Key + "?ci-process=sensitive-content-recognition"
      try {
        this.ctx.curl(url, {
          headers: {
            Authorization,
            'content-type': 'text/xml'
          },
          //dataType: 'xml',
        }).then(back => {
          let xml_str = back.data.toString()
          //console.log('xml_str=', xml_str);
          let result = {}
          xmlreader.read(xml_str, (err, res) => {
            // 通过此方法来判断，该回调是同步方法，还是异步方法
            // var start = (new Date()).getTime();
            // while ((new Date()).getTime() - start < 3 * 1000) {
            //   continue;
            // }
            if (err) return console.log(err);
            let Error = res.Error;
            let r = res.RecognitionResult
            if (Error) {
              //如果图片校验出错
              result.Result = 3;
              result.Label = Error.Message.text();
            } else {
              // 图片正确校验
              if (r) {
                if (r.Result) {
                  result.Result = r.Result.text();
                }
                if (r.Label) {
                  result.Label = r.Label.text();
                }
                if (r.SubLabel && r.SubLabel.text) {
                  result.SubLabel = r.SubLabel.text();
                }
                if (r.Score) {
                  result.Score = r.Score.text();
                }
              }
            }
            //console.log('Label=', res.RecognitionResult.Label.text());
          });
          //console.log('result=', result);
          resolve(result);
        }).catch(err => {
          console.log(err);
          reject(err)
        })
      } catch (error) {
        console.log('error=', error);
        reject(error)
      }
    });
  }

  // 获取cos的Authorization-API
  getAuth(opt) {
    opt = opt || {};

    var SecretId = opt.SecretId;
    var SecretKey = opt.SecretKey;
    var KeyTime = opt.KeyTime;
    var method = (opt.method || opt.Method || 'get').toLowerCase();
    var queryParams = this.clone(opt.Query || opt.params || {});
    var headers = this.getSignHeaderObj(this.clone(opt.Headers || opt.headers || {}));

    var Key = opt.Key || '';
    var pathname;
    if (opt.UseRawKey) {
      pathname = opt.Pathname || opt.pathname || '/' + Key;
    } else {
      pathname = opt.Pathname || opt.pathname || Key;
      pathname.indexOf('/') !== 0 && (pathname = '/' + pathname);
    }
    //console.log('pathname=', pathname);

    // 如果有传入存储桶，那么签名默认加 Host 参与计算，避免跨桶访问
    if (!headers.Host && !headers.host && opt.Bucket && opt.Region) headers.Host = opt.Bucket + '.cos.' + opt.Region + '.myqcloud.com';
    //console.log('headers.Host', headers.Host);

    if (!SecretId) throw new Error('missing param SecretId');
    if (!SecretKey) throw new Error('missing param SecretKey');

    // 签名有效起止时间
    var now = Math.round(this.getSkewTime(opt.SystemClockOffset) / 1000) - 1;
    var exp = now;

    var Expires = opt.Expires || opt.expires;
    if (Expires === undefined) {
      exp += 60 * 60; // 签名过期时间为当前 + 60*60s
    } else {
      exp += (Expires * 1) || 0;
    }

    // 要用到的 Authorization 参数列表
    var qSignAlgorithm = 'sha1';
    var qAk = SecretId;
    var qSignTime = KeyTime || now + ';' + exp;
    var qKeyTime = KeyTime || now + ';' + exp;
    var qHeaderList = this.getObjectKeys(headers, true).join(';').toLowerCase();
    var qUrlParamList = this.getObjectKeys(queryParams, true).join(';').toLowerCase();

    // 签名算法说明文档：https://www.qcloud.com/document/product/436/7778
    // 步骤一：计算 SignKey
    var signKey = crypto.createHmac('sha1', SecretKey).update(qKeyTime).digest('hex');
    //console.log('步骤一：计算 SignKey=', signKey);

    // 步骤二：构成 FormatString
    var formatString = [method, pathname, this.obj2str(queryParams, true), this.obj2str(headers, true), ''].join('\n');
    //console.log('步骤二：构成 FormatString=', formatString);
    formatString = Buffer.from(formatString, 'utf8');


    // 步骤三：计算 StringToSign
    var res = crypto.createHash('sha1').update(formatString).digest('hex');
    var stringToSign = ['sha1', qSignTime, res, ''].join('\n');
    //console.log('步骤三：计算 StringToSign=', stringToSign);


    // 步骤四：计算 Signature
    var qSignature = crypto.createHmac('sha1', signKey).update(stringToSign).digest('hex');
    //console.log('步骤四：计算 Signature=', qSignature);

    // 步骤五：构造 Authorization
    var authorization = [
      'q-sign-algorithm=' + qSignAlgorithm,
      'q-ak=' + qAk,
      'q-sign-time=' + qSignTime,
      'q-key-time=' + qKeyTime,
      'q-header-list=' + qHeaderList,
      'q-url-param-list=' + qUrlParamList,
      'q-signature=' + qSignature
    ].join('&');
    //console.log('步骤五：构造 Authorization=', authorization);

    return authorization;
  }

  camSafeUrlEncode(str) {
    return encodeURIComponent(str)
      .replace(/!/g, '%21')
      .replace(/'/g, '%27')
      .replace(/\(/g, '%28')
      .replace(/\)/g, '%29')
      .replace(/\*/g, '%2A');
  }

  getObjectKeys(obj, forKey) {
    var list = [];
    for (var key in obj) {
      if (obj.hasOwnProperty(key)) {
        list.push(forKey ? this.camSafeUrlEncode(key).toLowerCase() : key);
      }
    }
    return list.sort(function (a, b) {
      a = a.toLowerCase();
      b = b.toLowerCase();
      return a === b ? 0 : (a > b ? 1 : -1);
    });
  };

  /**
   * obj转为string
   * @param  {Object}  obj                需要转的对象，必须
   * @param  {Boolean} lowerCaseKey       key是否转为小写，默认false，非必须
   * @return {String}  data               返回字符串
   */
  obj2str(obj, lowerCaseKey) {
    var i, key, val;
    var list = [];
    var keyList = this.getObjectKeys(obj);
    for (i = 0; i < keyList.length; i++) {
      key = keyList[i];
      val = (obj[key] === undefined || obj[key] === null) ? '' : ('' + obj[key]);
      key = lowerCaseKey ? this.camSafeUrlEncode(key).toLowerCase() : this.camSafeUrlEncode(key);
      val = this.camSafeUrlEncode(val) || '';
      list.push(key + '=' + val)
    }
    return list.join('&');
  };


  // 获取调正的时间戳
  getSkewTime(offset) {
    return Date.now() + (offset || 0);
  }

  getSignHeaderObj(headers) {
    var signHeaderObj = {};
    for (var i in headers) {
      var key = i.toLowerCase();
      if (key.indexOf('x-cos-') > -1 || signHeaders.indexOf(key) > -1) {
        signHeaderObj[i] = headers[i];
      }
    }
    return signHeaderObj;
  }

  isArray(arr) {
    return arr instanceof Array;
  }

  map(obj, fn) {
    var o = this.isArray(obj) ? [] : {};
    for (var i in obj) {
      if (obj.hasOwnProperty(i)) {
        o[i] = fn(obj[i], i);
      }
    }
    return o;
  }

  clone(obj) {
    return this.map(obj, function (v) {
      return typeof v === 'object' && v !== null ? clone(v) : v;
    });
  }

}

module.exports = CosService;